import numpy as np
import json
import os
from typing import Dict, List, Tuple


class UserInfoRecommender:
    def __init__(
        self,
        user_vector_path: str = "/home/ai/Recommendation_system/user_vector_data.json",
        template_vector_path: str = "/home/ai/Recommendation_system/template_vectors.json"
    ):
        """初始化基于用户信息的推荐器"""
        self.user_vector_path = user_vector_path
        self.template_vector_path = template_vector_path
        
        # 加载向量数据
        self.user_vectors: Dict[int, np.ndarray] = self._load_user_vectors()  # {user_id: 向量}
        self.template_vectors: Dict[int, np.ndarray] = self._load_template_vectors()  # {template_id: 向量}
        self.template_metadata: Dict[int, Dict] = self._load_template_metadata()  # 模板额外信息（可选）

    def _load_user_vectors(self) -> Dict[int, np.ndarray]:
        """加载用户向量JSON文件"""
        if not os.path.exists(self.user_vector_path):
            raise FileNotFoundError(f"用户向量文件不存在：{self.user_vector_path}")
        
        try:
            with open(self.user_vector_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            return {
                item["user_id"]: np.array(item["vector"], dtype=np.float32)
                for item in data
            }
        except Exception as e:
            raise RuntimeError(f"加载用户向量失败：{str(e)}")

    def _load_template_vectors(self) -> Dict[int, np.ndarray]:
        """加载模板向量JSON文件"""
        if not os.path.exists(self.template_vector_path):
            raise FileNotFoundError(f"模板向量文件不存在：{self.template_vector_path}")
        
        try:
            with open(self.template_vector_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            # 适配template_vectors.json的结构（包含完整向量或示例向量）
            template_vectors = {}
            for tpl in data.get("templates", []):
                tid = tpl["template_id"]
                # 优先使用完整向量，若无则用示例向量（实际应用中建议存储完整向量）
                vector = tpl.get("vector") or tpl.get("vector_sample")
                if vector:
                    template_vectors[tid] = np.array(vector, dtype=np.float32)
            
            return template_vectors
        except Exception as e:
            raise RuntimeError(f"加载模板向量失败：{str(e)}")

    def _load_template_metadata(self) -> Dict[int, Dict]:
        """加载模板元数据（如行业、名称等，用于过滤或增强推荐）"""
        # 实际应用中可从数据库或其他文件加载，这里简化处理
        return {}

    def calculate_similarity(
        self, 
        user_vector: np.ndarray, 
        template_vector: np.ndarray
    ) -> float:
        """计算用户向量与模板向量的余弦相似度"""
        if len(user_vector) != len(template_vector):
            raise ValueError(f"向量维度不匹配：用户向量{len(user_vector)}维，模板向量{len(template_vector)}维")
        
        # 向量已归一化时，点积等价于余弦相似度
        return float(np.dot(user_vector, template_vector))

    def recommend(
        self, 
        user_id: int, 
        top_n: int = 5, 
        exclude_ids: List[int] = None
    ) -> List[Tuple[int, float]]:
        """
        基于用户信息向量推荐模板
        :param user_id: 用户ID
        :param top_n: 推荐数量
        :param exclude_ids: 排除的模板ID列表（如已交互过的）
        :return: 推荐的模板ID及相似度 [(tid1, sim1), (tid2, sim2), ...]
        """
        exclude_ids = exclude_ids or []
        
        # 检查用户是否存在
        if user_id not in self.user_vectors:
            raise ValueError(f"用户{user_id}的向量数据不存在")
        
        user_vector = self.user_vectors[user_id]
        similarities = []
        
        # 计算与所有模板的相似度
        for tid, tpl_vector in self.template_vectors.items():
            if tid in exclude_ids:
                continue  # 排除已交互的模板
            
            try:
                sim = self.calculate_similarity(user_vector, tpl_vector)
                similarities.append((tid, sim))
            except Exception as e:
                print(f"计算模板{tid}相似度失败：{e}")
                continue
        
        # 按相似度降序排序并取前N
        similarities.sort(key=lambda x: x[1], reverse=True)
        return similarities[:top_n]
